<?xml version="1.0" encoding="UTF-8"?>

<!-- Definition of an automata that echos a given C# file, but with all extra whitespace outside of comments and strings removed -->

<Automata namespaceName="Condenser" className="CSCondenser" baseClass="Condenser">

  <!-- literal code added to the generated class -->
	<ClassCode>
    // returns true iff c is an identifier character (alphanumeric or '_')
		protected bool isIdent(char c) {
		  return (('A'&lt;=c&amp;&amp;c&lt;='Z')||('a'&lt;=c&amp;&amp;c&lt;='z')||('0'&lt;=c&amp;&amp;c&lt;='9')||(c=='_'));
		}
	</ClassCode>

	<ClassCode>
    // makes all line breaks into &quot;\n&quot; format
    protected string fixLineBreaks(string s) {
      StringWriter output=new StringWriter();
      int i=0;
      for(;i+1&lt;s.Length;++i) {
        string ss=s.Substring(i,2);
        if(ss=="\r\n"||ss=="\n\r") {
          output.Write('\n');
          ++i;
          continue;
        }
        else if(s[i]=='\r')
          output.Write('\n');
        else
          output.Write(s[i]);
      }
      if(i&lt;s.Length) {
        if(s[i]=='\r')
          output.Write('\n');
        else
          output.Write(s[i]);
      }
      return output.ToString();
    }
	</ClassCode>

  <PreProcessCode>
      input=fixLineBreaks(input);
  </PreProcessCode>

  <!-- start state, that of normal code, outside of any comments or string literals, and not proceded by an identifier char -->
	<State stateName="CODE" initial="true">
		<Trans string="//" next="COMMENT_SINGLE"/>
		<Trans string="/*" next="COMMENT_MULTI"/>
		<Trans char="&quot;" next="NORMAL_STRING"/>
    <Trans char="\'" next="CHAR"/>
		<Trans string="@\&quot;" next="VERB_STRING"/>
		<Trans testMethod="isIdent" next="CODE_IDENT"/> <!-- if we se an identifier character, then move to a different state so that
                                                           we leave 1 space between identifer chars -->
		<Trans charSet=" \t\n" next="CODE" echo="false"/> <!-- if we encounter whitespace in this state, it did not start with an identifier character,
                                                                so just ignore it and go on -->
		<Trans />
	</State>

  <!-- identifier char (alpha-numeric or '_'). This is a separate state so that whitespace between identifier chars can be collapsed
        to one space, instead of removed completely. -->
	<State stateName="CODE_IDENT">
		<Trans testMethod="isIdent"/>
		<Trans charSet=" \t\n" next="CODE_IDENT_WS" echo="false"/>
		<Trans next="CODE" replace="true" echo="false"/>
	</State>

  <!-- whitespace that started with an identifier char. If it also ends with an identifier char, it is collapsed to 1 space,
      otherwise removed completely -->
	<State stateName="CODE_IDENT_WS">
    <!-- an identifier char was found, so print 1 space, and go back to code mode -->
		<Trans testMethod="isIdent" next="CODE" replace="true" echo="false">output.Write(' ');
		</Trans>
		<Trans charSet=" \t\n" echo="false"/>
		<Trans next="CODE" replace="true" echo="false"/>
	</State>
	
  <!-- single line comment -->
	<State stateName="COMMENT_SINGLE">
		<Trans charSet="\n" next="CODE"/>
		<Trans/>
	</State>
	
  <!-- multi line comment -->
	<State stateName="COMMENT_MULTI">
		<Trans string="*/" next="CODE"/>
		<Trans/>
	</State>
	
  <!-- normal string ("blah", not @"blah")-->
	<State stateName="NORMAL_STRING">
		<Trans char="&quot;" next="CODE"/>
		<Trans char="\\" next="NORMAL_STRING_ESC"/>
		<Trans />
	</State>
	
  <!-- escaped char in a normal string. just echo the next char and return to the normal string state -->
	<State stateName="NORMAL_STRING_ESC">
		<Trans next="NORMAL_STRING"/>
	</State>
	
  <!-- verbatim string (@"blah") -->
	<State stateName="VERB_STRING">
		<Trans string="\&quot;\&quot;"/> <!-- "" does not end the verb string, as this is an escaped '"' char -->
		<Trans char="&quot;" next="CODE"/>
		<Trans />
	</State>

  <!-- char literal -->
	<State stateName="CHAR">
    <Trans char="\\" next="CHAR_ESC"/>
    <Trans char="\'" next="CODE"/>
    <Trans/>
  </State>

  <!-- escaped char literal -->
	<State stateName="CHAR_ESC">
    <Trans next="CHAR"/>
  </State>

</Automata>

